import java.awt.Button;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.Font;
import java.awt.Image;
import java.awt.List;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.text.DateFormat;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.Random;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;


public class GameWindow extends JFrame implements ActionListener,MenuListener{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	//EASY level
	private int fieldCount = 9; // 9 normal 16, difficulty 25
	private int size = 130; 	// 130 normal 97, difficulty 77
	private int interval = 3;   // 3 normal 3, difficulty 3
	private int panelWidth = 403; //402 normal 403, difficulty 403
	
	private String level = "1";
	
	private ArrayList<Field> fields = new ArrayList<Field> (9);
	private JPanel panel = new JPanel();
	private boolean isStart = false;
	private LocalTime gameStartTime = null;
	private int disturbCount = 100;
	
	private String[][] rankData = new String[10][2];
	private int rankCount = 0;
	private JTable rankTable = new JTable(rankData,new String[]{"Rank","Completion Time (s)"});	
	
	public GameWindow(String string) {
		super(string);
		this.setIconImage(new ImageIcon("Penguins.jpg").getImage());
		this.setBounds(0, 0, 500, 500);
		this.setVisible(true);
		this.setResizable(false);
		this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
		JMenuBar menuBar = new JMenuBar();
		JMenu menu1 = new JMenu("Control",true);
		
		menu1.add(getMenuItem("Change difficulty"));
		menu1.add(getMenuItem("Ranking List"));
		menu1.add(getMenuItem("New game"));

		JMenu menu2 = new JMenu("Help");
		menu2.add(getMenuItem("About"));
		menu2.add(getMenuItem("Game rule"));
		menuBar.add(menu1);
		menuBar.add(menu2);
		menu1.addMenuListener(this);
		menu2.addMenuListener(this);
		menu1.setBounds(0, 0, 100, 20);
		menu2.setBounds(0,0,100,20);
		menuBar.setBounds(0, 0, 500, 20);
		
		panel.setBackground(new Color(0,255,255));
		panel.setBounds(50, 50, panelWidth, panelWidth);
		panel.setLayout(null);
		this.add(panel);
		this.setLayout(null);
		this.add(menuBar);
		
		addFields();
		updateFieldsPosition();
		
		getRankDataFromFile(level);
		this.addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosed(WindowEvent e) {
				saveRankDataToFile(level);				
			}
			
		});
	}

	private void saveRankDataToFile(String lev) {
		//save rank data to file
		FileOutputStream file;
		try {
			file = new FileOutputStream("history_rank_"+lev+".rec"); // if the file not found will create a new file
			DataOutputStream data = new DataOutputStream(file);
			data.writeInt(rankCount);
			for(int i=0; i<rankCount; i++) {
				data.writeInt(Integer.parseInt(rankData[i][0]));
				data.writeInt(Integer.parseInt(rankData[i][1]));
			}
			data.close();
			file.close();
		}catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

	}
	private void getRankDataFromFile(String lev) {		
		//file format
		//count int 4
		//rank int, time int
//		byte buffer[] = new byte[4];
		try {
			FileInputStream file =  new FileInputStream("history_rank_"+lev+".rec");
			DataInputStream data = new DataInputStream(file);			
			rankCount =  data.readInt();
			for(int i=0; i<rankCount; i++) {
				rankData[i][0] = String.valueOf(data.readInt());
				rankData[i][1] = String.valueOf(data.readInt());
			}
			data.close();
			file.close();
		}catch(FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			
			e.printStackTrace();
		}
	}

	private void addFields() {
		for(int i=0; i<fieldCount-1; i++) {
			Field field = new Field(i+1);
			this.fields.add(field);
			this.panel.add(field);
		}
		this.fields.add(null);
	}
	private void updateFieldsPosition() {		
		//this.panel.removeAll();// clear panel
		int index = 0;
		for(int j=0; j<Math.sqrt(fieldCount); j++) {
			for(int i=0; i<Math.sqrt(fieldCount); i++) {				
				Field field = fields.get(index++);
				if( field !=null ) {
					System.out.println(this.panel.getComponentCount());
					// if the position does not equal before position
					// setBounds() will move the field, you can see a movement 
					field.setBounds(i*size+(i+1)*interval, j*size+(j+1)*interval, size, size);  
				}
			}
		}		
		//this.panel.updateUI();
	}

	private JMenuItem getMenuItem(String string) {
		JMenuItem menuItem = new JMenuItem(string);
		menuItem.addActionListener(this);
		return menuItem;
	}

	public static void main(String args[]) {
		new GameWindow("game").run();
		
	}

	private void run() {
		
		
	}

	@Override
	public void menuCanceled(MenuEvent arg0) {
		
		
	}

	@Override
	public void menuDeselected(MenuEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void menuSelected(MenuEvent arg0) {	
//		JOptionPane.showMessageDialog(null, "Selected "+arg0.toString(), null, JOptionPane.INFORMATION_MESSAGE);
		if( arg0.toString().equals("Pause")) {
			JOptionPane.showMessageDialog(null, "Selected Pause", null, JOptionPane.INFORMATION_MESSAGE);
		}else if(arg0.toString().equals("Ranking List")) {
			JOptionPane.showMessageDialog(null, "Selected Ranking List", null, JOptionPane.INFORMATION_MESSAGE);
		}
		
	}

	@Override
	public void actionPerformed(ActionEvent arg0) {
		
//		String s = DateFormat.getTimeInstance(DateFormat.MEDIUM).format(new Date(arg0.getWhen()));
//		System.out.println(s);
//		System.out.println(arg0.getSource().toString());
		
//		JOptionPane.showMessageDialog(null, arg0.toString(),null,JOptionPane.INFORMATION_MESSAGE);
		switch(arg0.getActionCommand()) {
		case "Change difficulty":
//			JOptionPane.showMessageDialog(null, "Pause Click",null,JOptionPane.INFORMATION_MESSAGE);
//			JOptionPane.showOptionDialog(parentComponent, message, title, optionType, messageType, icon, options, initialValue)
			String chooses[] = new String[] {"EASY","NORMAL","DIFFICULTY"};
			String t = (String) JOptionPane.showInputDialog(null, "choose one","Configure",JOptionPane.INFORMATION_MESSAGE,
					null,chooses,"EASY");
//			System.out.println("diff = "+t);
			if( t!=null) {
				String beforeLevel = level;
				switch(t) {
				case "EASY":
					fieldCount = 9; // 9 normal 16, difficulty 25
					size = 130; 	// 130 normal 97, difficulty 77
//					interval = 3;   // 3 normal 3, difficulty 3
//					panelWidth = 402; //402 normal 403, difficulty 403
					level = "1";
					break;
				case "NORMAL":
					fieldCount = 16;
					size = 97;
					level = "2";
					break;
				case "DIFFICULTY":
					fieldCount = 25;
					size = 77;
					level = "3";
					break;
				
				}
				fields.clear();
				panel.removeAll();
				panel.updateUI();
				addFields();
				updateFieldsPosition();
				// file operation
				saveRankDataToFile(beforeLevel);
				clearRankData(rankData);
				getRankDataFromFile(level);
			}
			break;
		case "Ranking List":			
			JDialog dialog = new JDialog();
			dialog.setBounds(0, 0, 400, 400);
			dialog.setVisible(true);
			dialog.setTitle("Ranking List");
			dialog.setLayout(null);	
			JScrollPane scrollPane = new JScrollPane(rankTable);
			scrollPane.setBounds(40,40,300,300);			
//			rankData[0][0] = "1";
//			rankData[0][1] = "180s";
			
			dialog.add(scrollPane);
			dialog.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
			break;
		case "About":
			ImageIcon img = new ImageIcon("Koala.jpg");
			
			JOptionPane.showMessageDialog(null, "Hello World!", "About",
					JOptionPane.INFORMATION_MESSAGE, img);
			break;
		case "Game rule":
			JOptionPane.showMessageDialog(null, 
					  "click control->new game to start the game.\n"
					+ "click control->ranking list to view the rank\n"
					+ "when you firstly click the digital square,the time will\n"
					+ "be start until you take the square to right place.\n"
					+ "At this time,you will get a completion time,and if you break\n"
					+ "the record,you will see a completion time here with a rank.",
					"Game rule",JOptionPane.INFORMATION_MESSAGE);
			break;
		case "New game":
			isStart = true;
			disturbFields();
			default:
		}
		
	}
	
	private void clearRankData(String[][] rankData2) {
		for(int i=0; i<rankCount; i++) {
			rankData[i][0] = null;
			rankData[i][1] = null;			
		}		
	}

	private void disturbFields() {
		Random random = new Random(LocalTime.now().toNanoOfDay());
		ArrayList<Field> canMove = new ArrayList<> ();
		for(int i=0; i<disturbCount; i++) {
			for(int k=0; k<fieldCount; k++) {				
				Field field = fields.get(k);
				if( field!=null && findEmpty(k+1)!=0) {
					canMove.add(field);
				}
			}			
			int pos = fields.indexOf(canMove.
					get(random.nextInt(canMove.size())))+1;
			int newPos = findEmpty(pos);
			swap(pos,newPos);
			canMove.clear();
		}
		updateFieldsPosition();
	}

	class Field extends JButton implements ActionListener{
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
//		int position = 0;  
		int ID = 0;
		
		public Field(int id) {		
			super(new Integer(id).toString());
			this.ID = id;
			this.setFont(new Font(null,Font.BOLD,30));
			this.addActionListener(this);
		}

		@Override
		public void actionPerformed(ActionEvent arg0) {
			if( isStart && gameStartTime == null) {
				gameStartTime = LocalTime.now();
			}
//			JOptionPane.showMessageDialog(null, arg0.getActionCommand()
//					,null,JOptionPane.INFORMATION_MESSAGE);
			int pos = fields.indexOf(arg0.getSource())+1; // start from 1
			int newPos = 0;
			if( (newPos=findEmpty(pos))!=0 ) {
				swap(pos,newPos); // had changed the fields, panel should be redraw
				updateFieldsPosition();
				if( isStart && checkFields() ) {
					Integer time = LocalTime.now().toSecondOfDay()-gameStartTime.toSecondOfDay();
					JOptionPane.showMessageDialog(null, "Congratulations,you are successful.\n"+
					"You spent "+time.toString()+"s to finish it."
						,"Messages",JOptionPane.INFORMATION_MESSAGE);	
					isStart = false;
					gameStartTime = null;
					updateRankData(time);
				}
			}
			
		}
	}

	public int findEmpty(int pos) {
		int ret = 0;
		int t = (int) Math.sqrt(fieldCount);
		int aroundPos[] = {pos-1,pos+1,pos-t,pos+t};
		for(int i=0; i<aroundPos.length; i++) {
			if( aroundPos[i]>0 && aroundPos[i]<=fieldCount && this.fields.get(aroundPos[i]-1)==null) {
				ret = aroundPos[i]; // only can find one
				break;
			}
		}
		return ret;
	}

	public void updateRankData(Integer time) {
		if( rankCount<10) {
			rankData[rankCount][0] = new Integer(rankCount+1).toString();
			rankData[rankCount][1] = time.toString();
			rankCount++;
		}else if(time < Integer.parseInt(rankData[9][1])) {
			int i = 8;
			for( ; i>=0 && time<Integer.parseInt( rankData[i][1]); i--) {				
				rankData[i+1][1] = rankData[i][1];
			}
			rankData[i+1][0] = time.toString();
		}		
	}

	public boolean checkFields() {
		boolean isSucceed = true;
		for(int i=0; i<fieldCount; i++) {
			if( fields.get(i)!=null && fields.get(i).ID != i+1 ) {
				isSucceed = false;
				break;
			}
		}
		return isSucceed;
	}

	public void swap(int pos, int newPos) {
		fields.set(newPos-1, fields.get(pos-1));
		fields.set(pos-1, null);		
	}

}

//1 2 3
//4 5 6
//7 8 9

